import path from 'path';
import fs from 'fs';
import multiparty from 'multiparty';
import { exec } from 'child_process';
import { formatReqLog, verifyAuthentication, mkdirs, replaceSpecialCharacters } from '../util/';

let resultArr = [] as any;

const upload = async (req: any, res: any) => {
  const mine = await verifyAuthentication(req.headers.authorization || req.headers.token) as { err, data; };
  if (!mine.data) return res.send(formatReqLog(401, 'token已过期', null, { goal: req.originalUrl }));
  const index = Number(req.body.indexChunks);
  const total = Number(req.body.totalChunks);
  const name = req.body.fileName;
  const dir = path.join(__dirname, `../static/temporary/${mine.data.user}/`);
  await mkdirs(dir);
  const url = path.join(__dirname, `../static/${mine.data.user}/`);
  await mkdirs(url);
  fs.stat(req.file.path, async (err, stats) => {
    if (err) {
      res.send(formatReqLog(500, '文件读取失败', err, { goal: req.originalUrl, user: mine.data.user }));
      return;
    }
    // 文件大小以字节为单位
    const fileSizeInBytes = stats.size;
    // 将文件大小转换为 KB 或 MB 等其他单位进行显示
    const fileSizeInMB = fileSizeInBytes / (1024 * 1024);
    if (total === 1 && fileSizeInMB <= 0.3) {
      const readStream = fs.createReadStream(req.file.path);
      const writeStream = fs.createWriteStream(url + name);
      readStream.pipe(writeStream); // 写入文件
      readStream.on('end', () => {
        fs.unlinkSync(req.file.path); // 删除临时文件
        res.send(formatReqLog(
          200,
          '上传成功',
          `staticFile/${mine.data.user}/${name}`,
          { goal: req.originalUrl, user: mine.data.user })
        );
      });
    } else {
      const is = path.join(dir, index.toString());
      await fs.rename(req.file.path, is, async (err) => {
        if (err) {
          await fs.renameSync(req.file.path, is);
          return;
        }
        fs.chmodSync(is, 0o777);
      });

      if ((index + 1) === total) {
        const writeStream = await fs.createWriteStream(url + name);
        for (let i = 0; i < total; i++) {
          const chunkPath = path.join(dir, i.toString());
          const data = await fs.readFileSync(chunkPath);
          await writeStream.write(data);
        }
        writeStream.end();

        writeStream.on('finish', () => {
          fs.rmdirSync(dir, { recursive: true });
          res.send(formatReqLog(
            200,
            '上传成功',
            `staticFile/${mine.data.user}/${name}`,
            { goal: req.originalUrl, user: mine.data.user })
          );
        });

        writeStream.on('error', (err) => {
          res.send(formatReqLog(500, '写入合并失败', err, { goal: req.originalUrl, user: mine.data.user }));
        });
      } else {
        res.send(formatReqLog(201, '文件上传中', name, { goal: req.originalUrl, user: mine.data.user }));
      }
    }
  });
};

const img = async (req: any, res: any) => {
  const mine = await verifyAuthentication(req.headers.authorization || req.headers.token) as { err, data; };
  if (!mine.data) return res.send(formatReqLog(401, 'token已过期', null, { goal: req.originalUrl }));
  const form = new multiparty.Form();
  const dir = path.join(__dirname, `../static/${mine.data.user}/img/`);
  mkdirs(dir); // 目录不存在即创建目录
  form.parse(req, async (err, fields, files) => {
    if (err) {
      res.send({ code: 300, msg: '读取失败' });
    } else {
      if (!files.file) res.send({ code: 400, msg: '读取文件失败' });
      else {
        await files.file.forEach((item: any) => {
          if (!item.originalFilename.match(/\.(jpg|jpeg|png|xbm|tif|pjp|apng|svgz|ico|tiff|gif|svg|jfif|webp|bmp)$/)) {
            res.send({ code: 400, msg: '只允许上传图片' });
            return;
          }
          const filePath = dir + item.originalFilename;
          const readStream = fs.createReadStream(item.path);
          const writeStream = fs.createWriteStream(filePath);
          readStream.pipe(writeStream); // 写入文件
          readStream.on('end', () => {
            fs.unlinkSync(item.path); // 删除临时文件夹中的图片
            res.send(formatReqLog(
              200,
              '上传成功',
              `staticFile/${mine.data.user}/img/${item.originalFilename}`,
              { goal: req.originalUrl, user: mine.data.user })
            );
          });
        });
      }
    }
  });
};

const video = async (req: any, res: any) => {
  const mine = await verifyAuthentication(req.headers.authorization || req.headers.token) as { err, data; };
  if (!mine.data) return res.send(formatReqLog(401, 'token已过期', null, { goal: req.originalUrl }));
  const index = Number(req.body.indexChunks);
  const total = Number(req.body.totalChunks);
  const name = req.body.fileName;
  const type = path.extname(req.body.fileName);
  const videoExtensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm', '.ogm', '.mpg', '.ogv', '.asx', '.mpeg', '.m4v'];
  if (!total || !name || !videoExtensions.includes(type)) {
    return res.send(formatReqLog(400, '传输格式错误！', { index, total, name, type, format: videoExtensions.join(',') }, { goal: req.originalUrl }));
  }
  const dir = path.join(__dirname, `../static/temporary/${mine.data.user}/`) + replaceSpecialCharacters(name);
  await mkdirs(dir);
  const url = path.join(__dirname, `../static/temporary/${mine.data.user}/`) + name;

  const is = path.join(dir, '/' + index.toString());
  await fs.rename(req.file.path, is, async (err) => {
    if (err) {
      await fs.renameSync(req.file.path, is);
      return;
    }
    fs.chmodSync(is, 0o777);
  });

  if ((index + 1) === total) {
    const writeStream = await fs.createWriteStream(url);
    for (let i = 0; i < total; i++) {
      const chunkPath = path.join(dir, i.toString());
      const data = await fs.readFileSync(chunkPath);
      await writeStream.write(data);
    }
    writeStream.end();

    writeStream.on('finish', () => {
      fs.rmdirSync(dir, { recursive: true });
      const time = new Date().getTime();
      const m3u8 = path.join(__dirname, `../static/${mine.data.user}/${replaceSpecialCharacters(name)}/`);
      mkdirs(m3u8);
      const cmd = `ffmpeg -i ${url} -c:v libx264 -hls_list_size 0 -c:a aac -strict -2 -f hls ${m3u8 + time}.m3u8`;
      exec(cmd, (err, stdout, stderr) => {
        if (err) {
          return res.send(formatReqLog(500, '分片失败', err || stderr, { goal: req.originalUrl, user: mine.data.user }));
        }
        exec(`move /Y ${url} ${path.join(__dirname, `../static/${mine.data.user}/${replaceSpecialCharacters(name)}${type}`)}`);
        formatReqLog(
          200,
          '上传成功',
          `staticFile/${mine.data.user}/${replaceSpecialCharacters(name)}/${time}.m3u8`,
          { goal: req.originalUrl, user: mine.data.user }
        );
      });
      res.send(formatReqLog(
        204,
        '上传成功,等待文件合并...文件大小决定时间长短1MB/1S',
        `staticFile/${mine.data.user}/${replaceSpecialCharacters(name)}/${time}.m3u8`,
        { goal: req.originalUrl, user: mine.data.user })
      );
    });

    writeStream.on('error', (err) => {
      res.send(formatReqLog(500, '合并失败', err, { goal: req.originalUrl, user: mine.data.user }));
    });
  } else {
    res.send(formatReqLog(201, '文件上传中', req.body.fileName, { goal: req.originalUrl, user: mine.data.user }));
  }
};

const download = (req: any, res: any) => {
  try { // encodeURIComponent()
    console.log(req.params.file, req.params.name);
    const filePath = path.join(__dirname, `../static/${decodeURIComponent(req.params.file)}/${req.params.name}`);
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return res.send(formatReqLog(400, '没有此文件', null, { goal: req.originalUrl }));
      }
      res.download(filePath, req.params.name);
    });
  } catch (err: any) {
    res.send(formatReqLog(500, err, null, { goal: req.originalUrl }));
  }
};

const find = async (req: any, res: any) => {
  resultArr = [];
  const list = [] as any;
  const mine = await verifyAuthentication(req.headers.authorization || req.headers.token) as { err, data; };
  if (!mine.data) return res.send(formatReqLog(401, 'token已过期', null, { goal: req.originalUrl }));
  const data = await getAllDirbyFilename('../static/');
  for (let i = 0; i < data.length; i++) {
    if (data[i].path.match(new RegExp(req.query.name))) {
      try {
        const name = data[i].path.split('static\\')[1];
        data[i].path = name.replaceAll('\\', '/');
      } catch (error) {
        const name = data[i].path.split('static/')[1];
        data[i].path = name;
      }
      const user = new RegExp(mine.data.user === 'sysadmin' || mine.data.user === 'admin' ? '.*' : `${mine.data.user}/`);
      if (data[i].path.match(/\//) && data[i].path.match(user) && data[i].size) list.push(data[i]);
    }
  }
  setTimeout(() => {
    res.send(formatReqLog(200, '查询成功', list, { goal: req.originalUrl, user: mine.data.user }));
  }, 300);
};

const del = async (req: any, res: any) => {
  const mine = await verifyAuthentication(req.headers.authorization || req.headers.token) as { err, data; };
  if (!mine.data) return res.send(formatReqLog(401, 'token已过期', null, { goal: req.originalUrl }));
  if (mine.data.jurisdiction >= 1 || req.body.path.split('/')[0] === mine.data.user) {
    const filePath = path.resolve(__dirname, `../static/${req.body.path}`);
    fs.readFile(filePath, 'utf8', err => {
      if (err) return res.send(formatReqLog(400, '没有此文件', null, { goal: req.originalUrl, user: mine.data.user }));
      fs.unlink(filePath, (err) => {
        if (err) return res.send(formatReqLog(300, '删除失败', null, { goal: req.originalUrl, user: mine.data.user }));
        res.send(formatReqLog(200, '删除成功', null, { goal: req.originalUrl }));
      });
    });
  } else {
    res.send(formatReqLog(403, '无权限修改', null, { goal: req.originalUrl, user: mine.data.user }));
  }
};

const getAllDirbyFilename = (dir: string) => {
  const dirPath = path.resolve(__dirname, dir);
  const files = fs.readdirSync(dirPath); // 该文件夹下的所有文件名称 (文件夹 + 文件)
  files.forEach(file => {
    const filePath = path.resolve(__dirname, `${dir}/${file}`); // 当前文件 | 文件夹的路径
    const more = fs.statSync(filePath); // 更多信息
    resultArr.push({
      path: filePath,
      size: more.size,
      upDate: more.mtime,
      createDate: more.ctime
    });
    // 继续递归文件夹
    if (fs.statSync(filePath).isDirectory()) {
      getAllDirbyFilename(filePath);
    }
  });

  return resultArr;
};

export = {
  upload,
  download,
  find,
  del,
  img,
  video
};
